<template>
    <div>
        <header class="top-header">
            <div class="title-wrap">
                <div class="main-title"> 视频监控平台</div>
                <div style="width: 100%;height: 1px;background: #666;margin-top: 8px;"></div>
                <div class="sub-title">实时预览</div>
            </div>
        </header>
        <div style="width: 100%;height: 10px;background: #eee;"></div>
        <div class="video-monitor">
            <!-- 左侧摄像头列表 -->
            <aside class="camera-bar" :class="{ collapsed: barCollapsed }">
                <el-card shadow="hover" class="tree-card">
                    <div class="bar-header">
                        <span v-show="!barCollapsed">监控点位</span>
                        <i :class="barCollapsed ? 'el-icon-caret-right' : 'el-icon-caret-left'"
                            @click="barCollapsed = !barCollapsed" />
                    </div>

                    <el-input v-show="!barCollapsed" v-model="filterText" placeholder="搜索" size="mini" clearable
                        style="margin-bottom: 10px; margin-top: 8px" />
                    <!-- 关键：滚动容器 -->
                    <div class="tree-scroll" v-show="!barCollapsed">
                        <el-tree ref="tree" :data="treeData" :props="defaultProps" :filter-node-method="filterNode"
                            highlight-current default-expand-all :render-content="renderNode" />
                    </div>
                </el-card>
            </aside>
            <!-- 右侧视频区 -->
            <main class="video-main" :class="{ fullscreen }">
                <div class="video-grid" :class="`grid-${screen}`">
                    <div v-for="i in screen" :key="i" class="video-cell"
                        :class="{ over: dragOverIndex === i - 1, playing: players[i - 1] }"
                        @dragover.prevent="dragOverIndex = i - 1" @dragleave="dragOverIndex = -1"
                        @drop="handleDrop($event, i - 1)">
                        <!-- 占位 -->
                        <div v-if="!players[i - 1]" class="placeholder">
                            <i class="el-icon-video-play" />
                            <p>拖拽摄像头到此处</p>
                        </div>

                        <div v-else class="player-box">
                            <video class="player" controls autoplay muted :ref="'videoPlayer' + i" />
                            <span class="title">{{ players[i - 1].name }}</span>
                            <i class="el-icon-circle-close close" @click="removePlayer(i - 1)" />
                        </div>
                    </div>
                </div>
                <div class="tool-row" v-if="!fullscreen">
                    <div style="display: flex; align-items: center">
                        <div class="btn-group">
                            <div v-for="item in list" :key="item.id" class="group_level"
                                :class="screen == item.id ? 'seledct' : ''" @click="selectGroup(item.id)">
                                {{ item.name }}
                            </div>
                        </div>
                        <div class="group_level" @click="toggleFullscreen">
                            <i :class="fullscreen ? 'el-icon-copy-document' : 'el-icon-full-screen'
                                " style="font-size: 20px" />
                        </div>
                    </div>
                </div>
            </main>
        </div>
    </div>

</template>

<script>
import Hls from 'hls.js'
import flvjs from 'flv.js'
import axios from 'axios'

export default {
    name: 'VideoMonitor',
    data() {
        return {
            /* 以下仅列关键字段，其余保持你原有 */
            defaultProps: { children: 'children', label: 'label' },
            treeData: [{ label: '车载摄像头', children: [] }, { label: '大华摄像头', children: [] }],
            filterText: '',
            list: [1, 2, 4, 9, 16, 20, 25].map(i => ({ id: i, name: String(i) })),
            screen: 4,
            barCollapsed: false,
            fullscreen: false,
            dragOverIndex: -1,
            draggedCamera: null,
            players: [],                 // 仅存摄像头对象，不存 url
            hlsInstances: {},            // hls 实例
            flvInstances: {},            // flv 实例
            cameraList: []
        }
    },
    created() {
        this.loadTwo()
        this.init()
    },
    mounted() {
        /* 原生全屏同步逻辑（你原来那段）复制过来即可，无改动 */
        const onFullscreenChange = () => {
            const isNativeFull = !!(
                document.fullscreenElement ||
                document.webkitFullscreenElement ||
                document.mozFullScreenElement ||
                document.msFullscreenElement
            )
            if (!isNativeFull && this.fullscreen) this.fullscreen = false
        }
        document.addEventListener('fullscreenchange', onFullscreenChange)
        document.addEventListener('webkitfullscreenchange', onFullscreenChange)
        document.addEventListener('mozfullscreenchange', onFullscreenChange)
        document.addEventListener('MSFullscreenChange', onFullscreenChange)
        this.$once('hook:beforeDestroy', () => {
            document.removeEventListener('fullscreenchange', onFullscreenChange)
            document.removeEventListener('webkitfullscreenchange', onFullscreenChange)
            document.removeEventListener('mozfullscreenchange', onFullscreenChange)
            document.removeEventListener('MSFullscreenChange', onFullscreenChange)
        })
    },
    beforeDestroy() {
        /* ----------  同时清理两种实例  ---------- */
        Object.values(this.hlsInstances).forEach((hls) => {
            if (hls) hls.destroy();
        });
        Object.values(this.flvInstances).forEach((flv) => {
            if (flv) flv.destroy();
        });
    },
    watch: {
        filterText(val) {
            this.$refs.tree.filter(val)
        }
    },
    methods: {
        /* ---------- 1. 左侧树 ---------- */
        async loadTwo() {
            try {
                const [res1, res2] = await Promise.all([
                    axios.get("http://192.168.100.10:18090/das/app/api/v1/product/devices", {
                        params: { appId: "app", secret: "022d981e118a44818fd0a84c", productName: "车载摄像头" },
                    }),
                    axios.get("http://192.168.100.10:18090/das/app/api/v1/product/devices", {
                        params: { appId: "app", secret: "022d981e118a44818fd0a84c", productName: "大华摄像头" },
                    }),
                ]);
                const format = (arr) =>
                    arr.map((i) => ({ ...i, label: i.deviceName, __url: "", __loading: false }));
                this.treeData[0].children = format(res1.data.data);
                this.treeData[1].children = format(res2.data.data);
            } catch (e) {
                console.error("接口报错", e);
            }
        },
        renderNode(h, { node, data }) {
            const isCam = data.productName;
            return h(
                "div",
                {
                    attrs: { draggable: isCam },
                    on: {
                        dragstart: (e) => isCam && this.handleDragStart(e, data),
                        dragend: (e) => isCam && this.handleDragEnd(e),
                    },
                },
                [node.label]
            );
        },
        filterNode(value, data) {
            if (!value) return true
            return data.label.indexOf(value) !== -1
        },
        handleDragStart(e, camera) {
            this.draggedCamera = camera
            e.target.classList.add('dragging')
        },
        handleDragEnd(e) {
            e.target.classList.remove('dragging')
        },
        async handleDrop(e, index) {
            console.log(e, index);
            e.preventDefault();
            this.dragOverIndex = -1;
            if (!this.draggedCamera) return;
            this.removePlayer(index);
            try {
                const url = await this.fetchPlayUrl(this.draggedCamera);
                this.$set(this.players, index, { ...this.draggedCamera, url });
                this.$nextTick(() => this.initVideoPlayer(index));
            } catch (e) {
                this.$message.error("获取视频地址失败：" + e.message);
            } finally {
                this.draggedCamera = null;
            }
        },

        /* ---------- 2. 地址获取（带缓存） ---------- */
        async fetchPlayUrl(camera) {
            if (camera.__url) return camera.__url;
            if (camera.__loading) return camera.__loading;
            camera.__loading = axios
                .get("http://192.168.100.10:18090/das/app/api/v1/device/shadows", {
                    params: { appId: "app", secret: "022d981e118a44818fd0a84c", deviceCode: camera.deviceCode },
                })
                .then((res) => {
                    const url = res?.data?.data?.[0]?.properties?.[1]?.value;
                    if (!url) return Promise.reject(new Error("后端返回空地址"));
                    camera.__url = url.trim();
                    return camera.__url;
                })
                .finally(() => {
                    camera.__loading = false;
                });
            return camera.__loading;
        },

        /* ---------- 3. 播放器初始化 ---------- */
        async initVideoPlayer(index) {
            const video = this.$refs[`videoPlayer${index + 1}`]?.[0];
            if (!video) return;
            let url = this.players[index]?.url;
            /* 空地址保护 – 拿不到就停止 */
            if (!url) {
                try {
                    url = await this.fetchPlayUrl(this.players[index]);
                    if (!url) throw new Error("地址为空");
                    this.$set(this.players[index], "url", url);
                } catch (e) {
                    console.error("[initVideoPlayer] 获取播放地址失败:", e);
                    this.$message.error("获取视频地址失败，请重试");
                    return;
                }
            }
            /* 销毁旧实例 */
            if (this.hlsInstances[index]) { this.hlsInstances[index].destroy(); delete this.hlsInstances[index] }
            if (this.flvInstances[index]) { this.flvInstances[index].destroy(); delete this.flvInstances[index] }
            video.src = ''

            const isFlv = url.includes('.flv') || url.includes('%2Eflv') || url.startsWith('ws:')
            if (isFlv && flvjs.isSupported()) {
                /* FLV 分支 */
                const flv = flvjs.createPlayer({ type: 'flv', url, isLive: true }, {
                    enableWorker: false, enableStashBuffer: true, stashInitialSize: 128 * 1024, lazyLoad: false
                })
                this.flvInstances[index] = flv
                flv.attachMediaElement(video)
                flv.load()
                flv.play().catch(console.error)
                flv.on(flvjs.Events.ERROR, (t, d) => {
                    console.error('FLV 错误', t, d)
                    if (t === flvjs.ErrorTypes.NETWORK_ERROR) setTimeout(() => this.initVideoPlayer(index), 1500)
                })
            } else if (url.includes('.m3u8') && Hls.isSupported()) {
                /* HLS 分支 */
                const hls = new Hls({ enableWorker: true, lowLatencyMode: true })
                this.hlsInstances[index] = hls
                hls.loadSource(url)
                hls.attachMedia(video)
                hls.once(Hls.Events.MANIFEST_PARSED, () => video.play().catch(console.error))
                hls.on(Hls.Events.ERROR, (e, data) => {
                    console.error('HLS 错误', data)
                    if (data.fatal) {
                        switch (data.type) {
                            case Hls.ErrorTypes.NETWORK_ERROR: hls.startLoad(); break
                            case Hls.ErrorTypes.MEDIA_ERROR: hls.recoverMediaError(); break
                            default: this.initVideoPlayer(index); break
                        }
                    }
                })
            } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
                /* Safari 原生 */
                video.src = url
                video.addEventListener('loadedmetadata', () => video.play().catch(console.error))
            } else {
                this.$message.error('浏览器不支持该格式')
            }
        },
        removePlayer(index) {
            if (!this.players[index]) return
            if (this.hlsInstances[index]) { this.hlsInstances[index].destroy(); delete this.hlsInstances[index] }
            if (this.flvInstances[index]) { this.flvInstances[index].destroy(); delete this.flvInstances[index] }
            this.$set(this.players, index, null)
        },

        /* ---------- 4. 其余原逻辑 ---------- */
        selectGroup(id) { this.screen = id },
        toggleFullscreen() {
            this.fullscreen = !this.fullscreen
            const el = this.$el.querySelector('.video-main')
            if (this.fullscreen) {
                (el.requestFullscreen || el.webkitRequestFullscreen || el.mozRequestFullScreen || el.msRequestFullscreen)?.call(el)
            } else {
                (document.exitFullscreen || document.webkitExitFullscreen || document.mozCancelFullScreen || document.msExitFullscreen)?.call(document)
            }
        },
        async init() {
            this.players = Array(25).fill(null)   // 最大 25 分屏
        }
    }
}
</script>

<style lang="scss" scoped>
::v-deep .el-card__body {
    padding: 0 !important;
}

::v-deep .el-card {
    border: none !important;
    box-shadow: none !important;
}

.btn-group {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-right: 12px;

    .group_level {
        width: 20px;
        /* 同宽 */
        height: 20px;
        /* 同高 */
        line-height: 16px;
        text-align: center;
        border: 2px solid #fff;
        color: #fff;
        cursor: pointer;
        font-size: 12px;
        transition: all 0.25s;

        &:hover {
            border-color: #00e0ff;
            color: #fff;
        }

        /* 选中态 */
        &.select {
            background: #00e0ff;
            border-color: #00e0ff;
            color: #000;
        }

        /* 全屏图标居中 */
        i {
            font-size: 12px;
            vertical-align: baseline;
        }
    }
}

.seledct {
    background-color: #00e0ff;
    border-color: #00e0ff;
}

/* 覆盖 Element 主色为亮青 */
.el-button--primary {
    background-color: #00e0ff;
    border-color: #00e0ff;
    color: #000;
}

.el-button--primary:hover {
    background-color: #00c8e6;
    border-color: #00c8e6;
}

.video-monitor {
    display: flex;
    height: calc(100vh - 85px);
    background: #02102b;
    color: #fff;
}

/* ========== 左侧栏 新配色 ========== */
.camera-bar {
    width: 220px;
    height: calc(100vh - 85px);
    background: #fff; // 深灰背景
    //   border-right: 1px solid #3f3f3f;
    //   color: #f5f5f5; // 高亮文字
    display: flex;
    flex-direction: column;
    transition: width 0.3s;

    &.collapsed {
        width: 80px;
    }

    .bar-header {
        height: 40px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 0 12px;
        font-size: 15px;
        font-weight: bold;
        border-bottom: 1px solid #ddd;
        padding-bottom: 8px;

        i {
            cursor: pointer;
            color: #00bcd4;

            &:hover {
                color: #29b6f6;
            }
        }
    }

    .camera-list {
        flex: 1;
        padding: 8px;
        overflow-y: auto;

        .camera-item {
            display: flex;
            align-items: center;
            gap: 8px;
            padding: 10px;
            margin-bottom: 6px;
            border-radius: 4px;
            background: #2b2b2b;
            border: 1px solid #3f3f3f;
            cursor: move;
            user-select: none;
            color: #e0e0e0;
            transition: background 0.2s, color 0.2s;

            &:hover {
                background: #00bcd4;
                color: #000;
            }

            &.dragging {
                opacity: 0.5;
            }
        }
    }
}

/* 滚动条 */
.camera-list::-webkit-scrollbar {
    width: 6px;
}

.camera-list::-webkit-scrollbar-thumb {
    background: #00bcd4;
    border-radius: 3px;
}

/* 右侧主内容 */
.video-main {
    flex: 1;
    display: flex;
    flex-direction: column;
    padding: 12px;

    &.fullscreen {
        position: fixed;
        left: 0;
        top: 0;
        width: 100vw;
        height: 100vh;
        z-index: 9999;
        background: #000;
    }

    .tool-row {
        height: 40px;
        display: flex;
        align-items: center;
        justify-content: flex-end;
    }
}

/* 视频网格 */
.video-grid {
    flex: 1;
    display: grid;
    gap: 8px; // 略微缩小间距

    &.grid-1 {
        grid-template-columns: 1fr;
        grid-template-rows: 1fr;
    }

    &.grid-2 {
        grid-template-columns: 1fr 1fr;
        grid-template-rows: 1fr;
    }

    &.grid-4 {
        grid-template-columns: 1fr 1fr;
        grid-template-rows: 1fr 1fr;
    }

    &.grid-9 {
        grid-template-columns: repeat(3, 1fr);
        grid-template-rows: repeat(3, 1fr);
    }

    .video-cell {
        background: #000;
        border: 1px dashed rgba(0, 188, 212, 0.4);
        border-radius: 4px; // 统一小圆角
        position: relative;
        display: flex;
        align-items: center;
        justify-content: center;
        overflow: hidden; // 避免视频圆角溢出
        transition: border 0.2s;

        &.over {
            border-style: solid;
            border-color: #00bcd4;
            background: rgba(0, 188, 212, 0.1);
        }

        &.playing {
            border: none;
        }

        .placeholder {
            text-align: center;
            color: #aaa;
            font-size: 13px;
        }

        .player-box {
            width: 100%;
            height: 100%;
            position: relative;

            .player {
                width: 100%;
                height: 100%;
                object-fit: fill;
                border-radius: 4px; // 与父级圆角保持一致
            }

            .title {
                position: absolute;
                left: 6px;
                top: 6px;
                background: rgba(0, 0, 0, 0.65);
                padding: 4px 8px;
                border-radius: 3px;
                font-size: 12px;
                line-height: 1;
            }

            .close {
                position: absolute;
                right: 6px;
                top: 6px;
                font-size: 18px;
                cursor: pointer;
                color: #f44336;
                transition: transform 0.2s;

                &:hover {
                    transform: scale(1.15);
                }
            }
        }
    }
}

/* 视频控制条样式细化 */
.player::-webkit-media-controls-panel {
    background-color: rgba(0, 0, 0, 0.6);
}

.player::-webkit-media-controls-play-button,
.player::-webkit-media-controls-mute-button,
.player::-webkit-media-controls-fullscreen-button {
    color: #fff;
    filter: brightness(1.4);
}

.player::-webkit-media-controls-current-time-display,
.player::-webkit-media-controls-time-remaining-display {
    color: #fff;
    font-size: 11px;
}

.player::-webkit-media-controls-timeline {
    height: 3px;
    border-radius: 2px;
}

.player::-webkit-media-controls-progress-bar {
    //   background-color: #00bcd4;
    height: 100%;
    border-radius: 2px;
}

.player::-webkit-media-controls-volume-slider {
    height: 3px;
    border-radius: 2px;
}

/* 全屏时控制条 */
.player:fullscreen::-webkit-media-controls-panel,
.player:-webkit-full-screen::-webkit-media-controls-panel {
    background-color: rgba(0, 0, 0, 0.7);
}

/* 左侧整个面板只有纵向溢出才滚 */
.camera-bar {
    overflow-y: auto; // 纵向
    overflow-x: hidden; // 横向禁止

    &::-webkit-scrollbar {
        width: 6px;
    }

    &::-webkit-scrollbar-thumb {
        // background: #00bcd4;
        border-radius: 3px;
    }
}

/* 让 tree 本身出现纵向滚动条 */
.tree-scroll {
    max-height: calc(100vh - 120px); // 120 ≈ 标题+搜索+padding
    overflow-y: auto;
    overflow-x: auto; // 如果节点文字超长也允许横向滚

    &::-webkit-scrollbar {
        width: 6px;
        height: 6px;
    }

    &::-webkit-scrollbar-thumb {
        // background: #00bcd4;
        border-radius: 3px;
    }
}

/* 如果以后把 camera-list 放开，也加上滚动 */
.camera-list {
    overflow: auto; // auto = 溢出才出现

    &::-webkit-scrollbar {
        width: 6px;
        height: 6px;
    }

    &::-webkit-scrollbar-thumb {
        // background: #00bcd4;
        border-radius: 3px;
    }
}

.video-grid {
    flex: 1;
    display: grid;
    gap: 8px;

    &.grid-1 {
        grid-template-columns: 1fr;
        grid-template-rows: 1fr;
    }

    &.grid-2 {
        grid-template-columns: 1fr 1fr;
        grid-template-rows: 1fr;
    }

    &.grid-4 {
        grid-template-columns: 1fr 1fr;
        grid-template-rows: 1fr 1fr;
    }

    &.grid-9 {
        grid-template-columns: repeat(3, 1fr);
        grid-template-rows: repeat(3, 1fr);
    }

    // ✅ 16 格：固定 4×4
    &.grid-16 {
        grid-template-columns: repeat(4, 1fr);
        grid-template-rows: repeat(4, 1fr);
    }

    // ✅ 20、25 仍用自适应（可选）
    &.grid-20 {
        grid-template-columns: repeat(5, 1fr);
        grid-template-rows: repeat(4, 1fr);
    }

    &.grid-25 {
        grid-template-columns: repeat(5, 1fr);
        grid-template-rows: repeat(5, 1fr);
    }
}

::v-deep .el-tree-node__label {
    white-space: nowrap; // 不换行
    display: inline-block; // 让宽度由内容决定
}

/* 如果树本身被限制宽度，也要允许横向溢出 */
::v-deep .el-tree {
    min-width: 100%;
    width: max-content; // 由最宽节点决定真实宽度
}

/* ------ 1. 字体整体小一号 ------ */
::v-deep .el-tree {
    font-size: 14px;
    /* 默认 14px→13px */
}

/* ------ 2. 节点行高加高 ------ */
::v-deep .el-tree-node__content {
    height: 30px !important;
    /* 默认 26px→34px */
    line-height: 30px !important;
}


/* ===== 顶部标题栏 ===== */
/* 标题容器 */
.top-header {
    width: 100%;
    height: 75px;
    /* 调整高度以更贴近设计 */
    background: #02102b;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
    position: relative;
    z-index: 2000;
}

/* 标题容器 */
.title-wrap {

    color: #fff;
}

.main-title {
    padding-left: 30px;
    font-size: 18px;
    /* 调整字体大小 */
    font-weight: bold;
    letter-spacing: 1px;
    /* 调整字间距 */
    color: #ffffff;
    margin-bottom: 4px;
    padding-top: 7px;

    // padding-bottom: 5px;
}

.sub-title {
    margin-left: 30px;
    font-size: 12px;
    /* 调整字体大小 */
    color: #000;
    /* 副标题颜色 */
    font-weight: 500;
    background: #fff;
    width: 100px;
    height: 26px;
    margin-top: 10px;
    line-height: 26px;
    text-align: center;
    border-top-left-radius: 5px;
    border-top-right-radius: 5px;
}
</style>